#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "block_queue.h"
#include "hash.h"
//typedef void * (hash_iter_fun_t)(hash_elt_t*);

void free_elt(void *p){
  if(!p) return;
  queue_t *q = (queue_t*)p; 
  hash_elt_t *elt = queue_data(q,hash_elt_t,queue);
  //printf("free_elt from hash :%p\n",&*elt);
  free(elt);  
}

hash_t * hash_init(int size){
   hash_t *h = (hash_t*)malloc(sizeof(hash_t*)); 
   printf("init hash:%p\n",&*h);
   h->buckets = (queue_t**)malloc(sizeof(queue_t*)*size);
   int i;
   for(i=0;i<size;i++){
      h->buckets[i] = (queue_t*)malloc(sizeof(queue_t*));
      queue_init(h->buckets[i]);
   }
   h->size = size;
   h->count = 0;
   return h;
}

unsigned int hash_key(char *data, size_t len)  {
  unsigned int  i, key;  
  key = 0;  
  
  for (i = 0; i < len; i++) {
    key = hash(key, data[i]);  
  }  
  return key;  
}

hash_iter_t * hash_iter_init(hash_t *hash,hash_iter_t *hash_iter){
	if(!hash_iter) return NULL;
	hash_iter->cur_bucket=0;
	hash_iter->hash = hash;
	queue_t *q  = hash_iter->hash->buckets[hash_iter->cur_bucket];
	hash_iter->elt = queue_head(q);
	return hash_iter;
}

hash_elt_t * hash_iter_next(hash_iter_t *hash_iter){
	if(!hash_iter || !hash_iter->hash) return NULL;
	queue_t *q = hash_iter->hash->buckets[hash_iter->cur_bucket];
	queue_t *p = hash_iter->elt;
	while(p == queue_sentinel(q)){
		hash_iter->cur_bucket++;
		if(hash_iter->cur_bucket == hash_iter->hash->size)
			return NULL;
		q = hash_iter->hash->buckets[hash_iter->cur_bucket];
		p = queue_head(q);
	}
	hash_iter->elt = queue_next(p);
	return queue_data(p,hash_elt_t,queue);
}

hash_elt_t * hash_find_elt(hash_t *hash,char *key){
   //printf("hash:%p\n",&*hash);
   if(!hash || !hash->buckets) return NULL;
   unsigned int hashcode = hash_key(key,strlen(key));
   queue_t *q = hash->buckets[hashcode%hash->size]; 
   if(!q) return NULL;
   queue_t *p = queue_head(q); 
   for(;p != queue_sentinel(q) ; p = queue_next(p) ){
      hash_elt_t * ret = queue_data(p,hash_elt_t,queue); 
      if(strcmp(ret->name,key)==0)
         return ret;
   }
   return NULL;
}

void* hash_find(hash_t *hash,char *key){
   hash_elt_t *elt = hash_find_elt(hash,key);
   if(!elt) return NULL;
   return elt->data;
}


unsigned int hash_push(hash_t *hash,char *key,void *value){
   if(!hash || !hash->buckets) return -1;
   hash_elt_t *elt = hash_find_elt(hash,key);
   if(elt){
      elt->data = value;
      return hash->count;
   }
   elt = (hash_elt_t*)malloc(sizeof(hash_elt_t));
   memset(elt,0,sizeof(hash_elt_t));
   elt->name = key;
   elt->data = value;

   unsigned int hashcode = hash_key(key,strlen(key));
   queue_t *q = hash->buckets[hashcode%hash->size]; 
   queue_insert_head(q,&elt->queue); 
   return hash->count++;
}

// the free_func defined in block_queue.h
void hash_destory(hash_t *hash){
   if(!hash || !hash->buckets) return;
   unsigned int i=0; 
   for(; i<hash->size ; i++){
      queue_t *q = hash->buckets[i];
      queue_destory(q,free_elt);
      //printf("hash_destory queue of buckets :%p\n",&*q);
      free(q);
   }
   //printf("hash_destory buckets :%p\n",&*hash->buckets);
   free(hash->buckets);
   hash->buckets = NULL;
   //printf("hash_destory hash :%p\n",&*hash);
   free(hash);
   //hash = NULL;
}
